#ifndef __ARCH_IOAPIC_H
#define __ARCH_IOAPIC_H

#include <arch/interrupt.h>
#include <lib/list.h>
#include <arch/acpi.h>
#include <os/hardirq.h>

#define ISA_DEVICE_VECTOR 0x20

#define IOAPIC_MAP_SIZE 0x10000

// default interrupt dispatch destination
#define IOAPIC_DEFAULT_INT_DISPATCH_DEST 0 // default cpu 0

// direct access register
#define IOAPIC_REG_IDX 0x00
#define IOAPIC_REG_DATA 0x10
#define IOAPIC_REG_IRQASSERT 0x20
#define IOAPIC_REG_EOF 0x40

#define IOAPIC_VER_VER(ver) ((ver)&0xff)
#define IOAPIC_VER_PRTNUM(ver) (((ver) >> 16) & 0xff)

#define IOAPIC_REG_ID_IDX 0x00  // APIC ID rester
#define IOAPIC_REG_VER_IDX 0x01 // version regsiter
#define IOAPIC_REG_PRT_IDX 0x10 // programe redirector talbe register

#define IOAPIC_PRT_NUM_MAX 24

#define IOAPIC_PRT_DESTINATION_SHIFT 56     // prt destination
#define IOAPIC_PRT_INTMASK_SHIFT 16         // prt interrupt mask
#define IOAPIC_PRT_TRIGMODE_SHIFT 15        // prt trigger mode
#define IOAPIC_PRT_TRIGMODE_EDGE 0          // edge trigger
#define IOAPIC_PRT_TRIGMODE_LEVEL 1         // level trigger
#define IOAPIC_PRT_REMOTEIRR 14             // prt remote irr
#define IOAPIC_PRT_INTPINPOL_SHIFT 13       // prt pin polarity
#define IOAPIC_PRT_INTPINPOL_HIGH 0         // high polarity
#define IOAPIC_PRT_INTPINPOL_LOW 1          // low porarity
#define IOAPIC_PRT_DELIVERY_STATUS_SHIFT 12 // prt delivery status
#define IOAPIC_PRT_DESTMODE_SHIFT 11        // prt destination mode
#define IOAPIC_PRT_DESTMODE_PHYSICAL 0      // physical destination
#define IOAPIC_PRT_DESTMODE_LOGICAL 1       // logical destination
#define IOAPIC_PRT_DELIVERY_MODE_SHIFT 8    // prt delivery mode
#define IOAPIC_PRT_DELIVERY_MODE_FIXED 0x0  // send to fixed cpu,vector is 0
#define IOAPIC_PRT_DELIVERY_MODE_NMI 0x4    // no mask interrupt
#define IOAPIC_PRT_DELIVERY_MODE_INIT 0x5   // init interrupt
#define IOAPIC_PRT_DELIVERY_MODE_EXINT 0x7  // send a exint which come from pic
#define IOAPIC_PRT_INT_MASK 0x01            // mask interrupt
#define IOAPIC_PRT_INT_NOMASK 0x00          // enable interrupt
#define IOAPIC_PRT_INTVEC_SHIFT 0           // prt vector

typedef struct
{
    uint8_t cpuid; // processor id
    uint8_t pin;
    uint8_t irq;
    uint8_t vector;
} irq2pin_map_t;

typedef struct
{
    list_t list;
    uint8_t ioapic_id;
    uint32_t iobase;
    uint32_t gsi_base;
    uint32_t prt_num;
} ioapic_t;

extern irq2pin_map_t irq2pin_map[IRQ_NUM_MAX];

extern void IrqEntryGeneral();

#define IRQ_GENERAL_SIZE 0x32 // irq general handler function size

int IoAPICInit();
void IoAPICGetPRTMax(ioapic_t *ioapic);
void IoAPICMap(ioapic_t *ioapic, uint32_t iobase);
void IoAPICUnMap(ioapic_t *ioapic);
void IoAPICWriteReg(ioapic_t *ioapic, uint32_t idx, uint32_t data);
uint32_t IoAPICReadReg(ioapic_t *ioapic, uint32_t idx);
uint64_t IoAPICReadPRTReg(ioapic_t *ioapic, uint8_t prt_idx);
void IoAPICWritePRTReg(ioapic_t *ioapic, uint8_t prt_idx, uint64_t data);
void IoAPICSetPRT(ioapic_t *ioapic, uint8_t prt_idx, uint8_t vector, uint8_t dest, uint8_t delivery_mode, uint8_t dest_mode, uint8_t trig_mode, uint8_t level, uint8_t int_mask);
ioapic_t *IoAPICFind(uint8_t id);
void IoAPICPRTDisable(ioapic_t *ioapic, int prt_idx);
void IoAPICPRTEnalbe(ioapic_t *ioapic, int prt_idx);
void IoAPICPRTSetVec(ioapic_t *ioapic, int prt_idx, uint8_t vec);
void IoAPICDisableAll(ioapic_t *ioapic);
void IoAPICEnableAll(ioapic_t *ioapic);
void IoAPPICPRTInit(ioapic_t *ioapic);
void Irq2PinMapInit(ioapic_t *ioapic);
void Irq2PinMapBind(uint8_t pin, uint8_t irq);
uint8_t IoAPICGetGSI(ioapic_t *ioapic, uint8_t pin);
void IoAPICIrqRegister(ioapic_t *ioapic, uint8_t irq);
void IoAPICIrqUnReigster(ioapic_t *ioapic, uint8_t irq);
void IoAPICSendEOF(irqno_t irq);
void IoAPICSendEOF(irqno_t irq);

#endif